package org.subside.List;

import org.subside.List.pojo.Person;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @Author : zhangbo
 * @data : 2024/5/16
 * @description :归约(reduce)归约，也称缩减，顾名思义，是把一个流缩减成一个值，能实现对集合求和、求乘积和求最值操作
 */
public class StreamTest6 {
    public static void main(String[] args) {
        //1.求Integer集合的元素之和、乘积和最大值。
        // 求和方式1
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);

        Optional<Integer> sum11 = list.stream().reduce((x, y) -> x + y);
        // 求和方式2
        Optional<Integer> sum22 = list.stream().reduce(Integer::sum);
        // 求和方式3   reduce(参数一：标识，从0开始累加，也可以自定义数值操作,参数二)
        Integer sum33 = list.stream().reduce(0, Integer::sum);

        // 求乘积
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);

        // 求最大值方式1
        Optional<Integer> maxnum = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值写法2  此处reduce()参数1是什么作用？
        Integer maxnum2 = list.stream().reduce(1, Integer::max);

        System.out.println("list求和：" + sum11.get() + "," + sum22.get() + "," + sum33);
        System.out.println("list求积：" + product.get());
        System.out.println("list求最大值：" + maxnum.get() + "," + maxnum2);

        System.out.println("-===========================================================");
        /**
         * 实际案例：求所有员工的最高工工资和最低工资
         */
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        // 求工资之和方式1：
        Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        // 求工资之和方式2：
        Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
                (sum1, sum2) -> sum1 + sum2);
        // 求工资之和方式3：
        Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);

        // 求最高工资方式1：
        Integer maxSalary = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
                Integer::max);
        // 求最高工资方式2：
        Integer maxSalary2 = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
                (max1, max2) -> max1 > max2 ? max1 : max2);
        // 求最高工资方式3：
        Integer maxSalary3 = personList.stream().map(Person::getSalary).reduce(Integer::max).get();

        System.out.println("工资之和：" + sumSalary.get() + "," + sumSalary2 + "," + sumSalary3);
        System.out.println("最高工资：" + maxSalary + "," + maxSalary2 + "," + maxSalary3);
//        Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
//        在这段代码中，`reduce` 方法用于在流 `personList.stream()` 上进行归约操作。`reduce` 方法接受三个参数：
//        1. 初始累加值（identity value）：这里是 `0`，表示累加的初始值是 `0`。
//        2. 一个二元操作符，用于更新累加值：这里是 `(sum, p) -> sum += p.getSalary()`，表示对每个 `Person` 对象 `p`，将其薪水 `p.getSalary()` 加到当前的累加值 `sum` 上。
//        3. 一个二元操作符，用于组合两个累加值：这里是 `Integer::sum`，表示如果存在并行处理，将使用 `Integer::sum` 方法来合并部分累加的结果。
//        具体来说：
//        - `sum` 是当前的累加值，初始时为 `0`。
//        - `p` 是流中的元素，即 `Person` 对象。
//        `(sum, p) -> sum += p.getSalary()` 这个函数的作用是将 `Person` 对象 `p` 的薪水累加到 `sum` 上。这里的 `+=` 操作符实际上是一个简写，
//        等同于 `sum = sum + p.getSalary()`。
//        最终，`reduce` 方法会返回一个 `Integer` 类型的累加结果，表示 `personList` 中所有 `Person` 对象薪水的总和。
//        需要注意的是，由于 `reduce` 方法的第二个参数是一个二元操作符，它接受两个参数并返回一个结果。在这个例子中，`(sum, p) -> sum += p.getSalary()`
//        可以简化为 `(a, b) -> a + b.getSalary()`，因为 `a` 代表当前的累加值，而 `b` 代表流中的下一个元素。
//        完整的 `reduce` 调用可以简化为：
//        Integer sumSalary3 = personList.stream()
//         .reduce(0, (a, b) -> a + b.getSalary(), Integer::sum);
//        这将产生与原始代码相同的效果。
    }
}
